package main

import (
	context "context"
	"fmt"
	"time"
)

/**
初识 Context
以上示例是 select+channel 比较经典的使用场景，这里也顺便复习了 select 的知识。
通过 select+channel 让协程退出的方式比较优雅，但是如果我们希望做到同时取消很多个协程呢？如果是定时取消协程又该怎么办？这时候 select+channel 的局限性就凸现出来了，即使定义了多个 channel 解决问题，代码逻辑也会非常复杂、难以维护。
要解决这种复杂的协程问题，必须有一种可以跟踪协程的方案，只有跟踪到每个协程，才能更好地控制它们，这种方案就是 Go 语言标准库为我们提供的 Context，也是这节课的主角。
*/
// 使用context控制多个协程之间的退出

//func main() {
//	var wg sync.WaitGroup
//	wg.Add(1)
//
//	ctx, stop := context.WithCancel(context.Background())
//	//cb :=context.Background()
//	go func() {
//		defer wg.Done()
//		watchDog3(ctx, "监控狗1")
//	}()
//
//	time.Sleep(5 * time.Second)
//	stop() // 发出停止指令
//	wg.Wait()
//}

func watchDog3(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("收到终止命令！！！终止协程", name)
			return
		default:
			fmt.Println(name, ":监控狗正在运行...")
		}
		time.Sleep(1 * time.Second) // 这段话必须写在for循环里面，不然永远执行不到
	}
}

/**
相比 select+channel 的方案，Context 方案主要有 4 个改动点。
watchDog 的 stopCh 参数换成了 ctx，类型为 context.Context。
原来的 case <-stopCh 改为 case <-ctx.Done()，用于判断是否停止。
使用 context.WithCancel(context.Background()) 函数生成一个可以取消的 Context，用于发送停止指令。
这里的 context.Background() 用于生成一个空 Context，一般作为整个 Context 树的根节点。
原来的 stopCh <- true 停止指令，改为 context.WithCancel 函数返回的取消函数 stop()。
可以看到，这和修改前的整体代码结构一样，只不过从 channel 换成了 Context。以上示例只是 Context 的一种使用场景，它的能力不止于此，现在我来介绍什么是 Context。
*/
